#ifndef TCURVE_H
#define TCURVE_H

#include <vector>
#include <algorithm>
#include <assert.h>

using namespace std;

template< class IPoint >
class TCurve
{

public:
    /* Constructor. */
    TCurve( );

    /* Destructor. */
    ~TCurve( );

    /* Methods. */
    void AddPoint( const IPoint& point );
    void AddPoint( const IPoint& point, int point_index );

    void RemovePoint( );
    void RemovePoint( int point_index );

    void MovePoint( int point_index, const IPoint& vector );
    void MoveCurve( const IPoint& vector );

    bool Exists( const IPoint& point );

    virtual IPoint Eval( double t ) const = 0;
    virtual IPoint* ComputePoints( int n ) const = 0;

    /* Getters. */
    vector< IPoint > ControlPoints( ) const;
    vector< IPoint > ConvexHull( ) const;

protected:
    /* Fields. */
    vector< IPoint > control_points;
};

template < class IPoint >
TCurve< IPoint >::TCurve()
{
    control_points = new vector< IPoint >();
}

template < class IPoint >
TCurve< IPoint >::~TCurve()
{
    delete control_points;
}

/* Methods. */
template < class IPoint >
void TCurve< IPoint >::AddPoint( const IPoint& point )
{
    control_points.push_back( point );
}

template < class IPoint >
void TCurve< IPoint >::AddPoint( const IPoint& point, int point_index )
{
    typename vector< IPoint >::const_iterator it = control_points.begin() + point_index;

    control_points.insert( it, 1, point );
}

template < class IPoint >
void TCurve< IPoint >::RemovePoint( )
{
    assert( !control_points.empty( ) );

    control_points.pop_back( );
}

template < class IPoint >
void TCurve< IPoint >::RemovePoint( int point_index )
{
    assert( point_index >= 0 && point_index < control_points.size( ) );

    typename vector< IPoint >::const_iterator it = control_points.begin( ) + point_index;

    copy( it + 1, control_points.end( ), // Source
          it );                          // Destination

    control_points.pop_back();
}

template < class IPoint >
void TCurve< IPoint >::MovePoint( int point_index, const IPoint& vector )
{
    control_points[point_index] = vector;
}

template < class IPoint >
void TCurve< IPoint >::MoveCurve( const IPoint& vector )
{
    for (unsigned int i = 0; i < control_points.size( ); i++)
        control_points[i] += vector;
}

template < class IPoint >
bool TCurve< IPoint >::Exists( const IPoint& point )
{
    typename vector< IPoint >::const_iterator it =
            find( control_points.begin( ), control_points.end( ), point );

    if ( it == control_points.end() )
        return false;

    return true;
}

template < class IPoint >
IPoint* TCurve< IPoint >::ComputePoints( int n ) const
{
    IPoint* points = new IPoint[n];

    for ( int i = 0; i < n; i++ )
    {
        points[i] = Eval( (double)i / (double)(n-1) );
    }

    return points;
}

/* Getters. */
template < class IPoint >
vector< IPoint > TCurve< IPoint >::ControlPoints( ) const
{
    return control_points;
}

template < class IPoint >
vector< IPoint > TCurve< IPoint >::ConvexHull( ) const
{
    // TODO!!!
}

#endif // TCURVE_H
